<?php
/**
 * Created by PhpStorm.
 * User: Admin
 * Date: 2020-08-02
 * Time: 下午 02:55
 */
namespace Lib;

/**
 * Class Server
 * @package Lib
 */
class UrlCreator extends Injector {
    protected $_originUrl;
    protected $_parseResult;
    protected $_originParseResult;

    public function __construct($url=null) {
        $url = $url ? $url : $_SERVER['REQUEST_URI'];
        $this->_originUrl = $url;
        $this->_parseResult = parse_url($url);
        $parseResult = &$this->_parseResult;
        
        if($parseResult['query']) {
            parse_str($parseResult['query'], $parseResult['query_params']);
        }
        
        if($parseResult['fragment']) {
            $this->_parseResult = array_merge($parseResult, $this->_parseFragment($parseResult['fragment']));
        }
        
        $this->_originParseResult = $this->_parseResult;
    }

    public function showUrl() {
        echo $this->toUrl();
    }
    
    public function reset() {
        $this->_parseResult = $this->_originParseResult;
        return $this;
    }
    
    protected function _parseFragment($fragment) {
        $parseResult = ['fragment' => $fragment];
        
        if(false !== ($pos = strpos($fragment, '?'))) {
            $fragmentQueryStr = substr($fragment, $pos+1);

            if($fragmentQueryStr) {
                parse_str($fragmentQueryStr, $parseResult['fragment_params']);
            }

            $parseResult['fragment_path'] = substr($fragment, 0, $pos);
        } else {
            $parseResult['fragment_path'] = $fragment;
        }
        
        return $parseResult;
    }

    public function toUrl() {
        $urlResult = '';
        $parseResult = &$this->_parseResult;
        
        if($parseResult['scheme']) {
            $urlResult .= "{$parseResult['scheme']}://";
        }
        
        $host = '';
        
        if($parseResult['user']) {
            $host .= $parseResult['user'];
        }
        
        if($parseResult['pass']) {
            $host .= ":{$parseResult['pass']}";
        }
        
        if($parseResult['user']) {
            $host .= '@';
        }
        
        if($parseResult['host']) {
            $host .= $parseResult['host'];
        }

        $host = $host ? "//$host" :  $host;
        $urlResult .= "$host/" . ltrim($parseResult['path'], '/\\');
        
        $queryStr = $this->getQueryStr();
        
        if($queryStr) {
            $urlResult .= "?$queryStr";
        }

        $fragment = $this->getFragment();
        
        if($fragment) {
            $urlResult .= "#$urlResult"; 
        }
        
        return $urlResult;
    }
    
    public function getQueryStr() {
        return http_build_query((array)$this->_parseResult['query_params']);
    }
    
    public function getFragmentQueryStr() {
        return http_build_query((array)$this->_parseResult['fragment_params']);
    }
    
    public function getHost() {
        return $this->_parseResult['host'];
    }
    
    public function getScheme() {
        return $this->_parseResult['scheme'];
    }

    public function getPort() {
        return $this->_parseResult['port'];
    }

    public function getUser() {
        return $this->_parseResult['user'];
    }

    public function getPass() {
        return $this->_parseResult['pass'];
    }

    public function getPath() {
        return $this->_parseResult['path'];
    }

    public function setHost($value) {
        $this->_parseResult['host'] = $value;
        return $this;
    }

    public function setScheme($value) {
        $this->_parseResult['scheme'] = $value;
        return $this;
    }

    public function setPort($value) {
        $this->_parseResult['port'] = $value;
        return $this;
    }

    public function setUser($value) {
        $this->_parseResult['user'] = $value;
        return $this;
    }

    public function setPass($value) {
        $this->_parseResult['pass'] = $value;
        return $this;
    }

    public function setPath($value) {
        $this->_parseResult['path'] = $value;
        return $this;
    }

    public function getFragment() {
        $str = '';
        
        if($this->_parseResult['fragment_path']) {
            $str .= "{$this->_parseResult['fragment_path']}";
        }

        if(!empty($this->_parseResult['fragment_params'])) {
            $str .= "?" . $this->getFragmentQueryStr();
        }
        return $str;
    }
    
    public function setFragment($fragment) {
        $this->_parseResult = array_merge($this->_parseResult, $this->_parseFragment($fragment));
        return $this;
    }
    
    public function getFragmentPath() {
        return $this->_parseResult['fragment_path'];
    }
    
    public function setFragmentPath($value) {
        $this->_parseResult['fragment_path'] = $value;
        return $this;
    }
    
    public function setFragmentQuerStr($str) {
        parse_str($str, $this->_parseResult['fragment_params']);
        return $this;
    }
    
    public function setQueryStr($str) {
        parse_str($str, $this->_parseResult['query_params']);
        return $this;
    }
    
    public function getQueryParams() {
        return $this->_parseResult['query_params'];
    }
    
    public function getFragmentParams() {
        return $this->_parseResult['fragment_params'];
    }
    
    public function getParseResult() {
        return $this->_parseResult;
    }
    
    public function setQueryParams($key, $value) {
        if(is_array($key)) {
            return $this->mergeQueryParams($key);
        }
        
        $this->_parseResult['query_params'] [$key] = $value;
        return $this;
    }

    public function setFragmentQueryParams($key, $value) {
        if(is_array($key)) {
            return $this->mergeFragmentQueryParams($key);
        }

        $this->_parseResult['fragment_params'] [$key] = $value;
        return $this;
    }
    
    public function removeQueryParam($key) {
        if(!is_array($key)) {
            $key = [$key];
        }
        
        foreach ((array)$key as $i => $k) {
            unset($this->_parseResult['query_params'][$k]);
        }
        
        return $this;
    }

    public function removeFragmentQueryParam($key) {
        if(!is_array($key)) {
            $key = [$key];
        }

        foreach ((array)$key as $i => $k) {
            unset($this->_parseResult['fragment_params'][$k]);
        }

        return $this;
    }

    public function mergeQueryParams($params) {
        $this->_parseResult['query_params'] = array_merge((array)$this->_parseResult['query_params'], $params);
        return $this;
    }

    public function mergeFragmentQueryParams($params) {
        $this->_parseResult['fragment_params'] = array_merge((array)$this->_parseResult['fragment_params'], $params);
        return $this;
    }

    public function toggleQueryParam($key, $value=1) {
        if($this->hasQueryParam($key)) {
            $this->removeQueryParam($key);
        } else {
            $this->setQueryParams($key, $value);
        }
        
        return $this;
    }

    public function toggleFragmentQueryParam($key, $value=1) {
        if($this->hasFragmentQueryParam($key)) {
            $this->removeFragmentQueryParam($key);
        } else {
            $this->setFragmentQueryParams($key, $value);
        }

        return $this;
    }
    
    public function hasQueryParam($key) {
        return isset($this->_parseResult['query_params'][$key]);
    }
    
    public function hasFragmentQueryParam($key) {
        return isset($this->_parseResult['fragment_params'][$key]);
    }
    
    public function toString() {
        return $this->toUrl();
    }
    
    public function __toString() {
        return $this->toString();
    }
}